// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
 * Description: make cmci parameters configurable
 * Author: Lv Ying
 * Create: 2020-03-25
 */

#include <linux/printk.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/uaccess.h>

#include "internal.h"
#include "euleros_cmci.h"

/*
 * IA32_MCi_CTL2 corrected error count threshold, bit [14:0]
 * Default value 1 means that one CE triggers one CMCI to OS.
 */
unsigned int g_ce_count_threshold = 1;

/*
 * Polling all banks 1 time/1 min in CMCI storm active status.
 * Original design of CMCI polling is using check_interval for 2x slower/faster.
 * However during CMCI storm, 2x faster will generate frequent clock interrupt
 * that lead to performance problem. So use fixed polling time interval.
 */
unsigned int g_poll_interval = 60;

/*
 * The threshold for CMCI storm detect
 * Default value 0 means OS switch to polling mode by receiving just one CMCI.
 */
int g_cmci_storm_threshold;

/*
 * The number(minimum) of poll times to switch back to interrupt mode.
 * The period is ([cmci_poll_times - 1] * cmci_poll_interval) seconds.
 * Because when storm detected, mce starts to poll 1s (CMCI_STORM_INTERVAL) later,
 * if no CE found at the 1st time of polling, the backoff counter decreased.
 */
int g_poll_times = INITIAL_CHECK_INTERVAL;

static int __init os_cmci_threshold(char *str)
{
	int ce_count_threshold = 0;

	get_option(&str, &ce_count_threshold);

	if (ce_count_threshold <= 0 || ce_count_threshold >= MAX_CMCI_THRESHOLD)
		pr_alert("CMCI threshold set err: %d( out of range: (0, %llu) ) !\n",
				ce_count_threshold, MAX_CMCI_THRESHOLD);
	else
		g_ce_count_threshold = (unsigned int)ce_count_threshold;

	pr_notice("CMCI threshold set to %u\n", g_ce_count_threshold);
	return 0;
}
__setup("cmci_threshold=", os_cmci_threshold);

static int __init os_cmci_poll_interval(char *str)
{
	unsigned int poll_interval = 0;

	get_option(&str, &poll_interval);

	if (poll_interval == 0 || poll_interval > MAX_CMCI_POLL_INTERVAL)
		pr_alert("CMCI poll interval set err: %u( out of range (0, %d] ) !\n",
				poll_interval, MAX_CMCI_POLL_INTERVAL);
	else
		g_poll_interval = poll_interval;

	pr_notice("CMCI poll interval set to %u\n", g_poll_interval);
	return 0;
}
__setup("cmci_poll_interval=", os_cmci_poll_interval);

static int __init os_cmci_storm_threshold(char *str)
{
	int cmci_storm_threshold = 0;

	get_option(&str, &cmci_storm_threshold);

	if (cmci_storm_threshold < 0 || cmci_storm_threshold > MAX_CMCI_STORM_THRESHOLD)
		pr_alert("CMCI storm threshold set err: %d( out of range: [0, %d] ) !\n",
				cmci_storm_threshold, MAX_CMCI_STORM_THRESHOLD);
	else
		g_cmci_storm_threshold = cmci_storm_threshold;

	pr_notice("CMCI storm threshold set to %d\n", g_cmci_storm_threshold);
	return 0;
}
__setup("cmci_storm_threshold=", os_cmci_storm_threshold);

static int __init os_cmci_poll_times(char *str)
{
	int poll_times = 0;

	get_option(&str, &poll_times);

	if (poll_times <= 0 || poll_times > INITIAL_CHECK_INTERVAL)
		pr_alert("CMCI poll times set err: %d( out of range: (0, %d] ) !\n",
				poll_times, INITIAL_CHECK_INTERVAL);
	else
		g_poll_times = poll_times;

	pr_notice("CMCI poll times set to %d\n", g_poll_times);
	return 0;
}
__setup("cmci_poll_times=", os_cmci_poll_times);
